#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>

//#define PING

#define DEFAULT_ADDRESS "127.0.0.1"
#define DEFAULT_PORT	4444
#define MAX_BUFF_SIZE   1500
#define MAX_CMD_SIZE 128

#define OK 0
#define ERROR -1

static pthread_t ThreadId;
static int ThreadLoop = 1;
static void * ServerThread(void * Arg);
static char * InetAddr = INADDR_ANY;
static int InetPort = DEFAULT_PORT;
static int ConnectSock = 0;
static int ListenSock = 0;


int main(int argc, char *argv[])
{
    int c = 0;
    opterr = 0;
    char Command[MAX_CMD_SIZE];

    // Parse option
     while ((c = getopt (argc, argv, "a:p:h")) != -1)
     {
 		switch (c)
 		{
		case 'a':
			if (optarg) InetAddr = optarg;
		break;

 		case 'p':
 			if (optarg) InetPort = atoi(optarg);
 		break;

 		case 'h':
 			printf("\n"
 				  "Server [options]\n"
 				  "\tOptions:\n"
 				  "\t\t-a  address       internet address, dotted-string"
 				  "\t\t-p  port          port number");
 		break;

 		default:
 			break;
 		}
     }

 	printf("Start up TCP server\n");

	// Start server thread
	if (pthread_create(&ThreadId, NULL, ServerThread, (void *) &InetPort) != 0)
	{
		return -1;
	}

	// CLI process
	while (1)
	{
		// Get the command
		printf("Command: ");
		scanf ("%s", Command);

		// Parse the command
		if (strcmp("quit", Command) == 0)
		{
			break;
		}

		if (strcmp("help", Command) == 0)
		{
			printf(	"help - to get the list of internal command\n"
					"quit - to quit this CLI without closing the server\n"
					);
			continue;
		}
	}

	ThreadLoop = 0;
	shutdown(ListenSock, SHUT_RDWR);
	shutdown(ConnectSock, SHUT_RDWR);

	pthread_join(ThreadId, NULL);

	printf("Bye bye!\n");

	return 0;
}

static void * ServerThread(void * Arg)
{
	int Error = 0;
	struct sockaddr_in ListenAddr;
	struct sockaddr_in ClientAddr;
    unsigned char Buffer[MAX_BUFF_SIZE];
    unsigned int opt = 1;

    // Create main socket
	ListenSock = socket(AF_INET, SOCK_STREAM, 0);
    if (ListenSock < 0)
    {
    	Error = errno;
    	pthread_exit(NULL);
    }

	if (setsockopt(ListenSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
	{
    	Error = errno;
	   	pthread_exit(NULL);
	}

    // Bind
    bzero((char *) &ListenAddr, sizeof(ListenAddr));
    ListenAddr.sin_family = AF_INET;
    ListenAddr.sin_addr.s_addr = inet_addr(InetAddr);
    ListenAddr.sin_port = htons(InetPort);
    if (bind(ListenSock, (struct sockaddr *) &ListenAddr, sizeof(ListenAddr)) < 0)
    {
    	Error = errno;
    	pthread_exit(NULL);
    }

    // Listen
    if(listen(ListenSock, 1) < 0)
    {
    	Error = errno;
    	pthread_exit(NULL);
    }

    while(ThreadLoop)
	{
		int Len = 0;
	    int RecvCount = 1;

	    printf("Waiting connections on %s:%d...\n", InetAddr, InetPort);

		// Accept
	    Len = sizeof(ClientAddr);
		ConnectSock = accept(ListenSock, (struct sockaddr *) &ClientAddr, (socklen_t*)&Len);
		if (ConnectSock < 0)
		{
			Error = errno;
			pthread_exit(NULL);
		}

	    printf("Connection accepted!\n");

		// Process communication
		while (RecvCount > 0)
		{
		    bzero((void *)Buffer,MAX_BUFF_SIZE);
		    RecvCount = recv(ConnectSock, (void *)Buffer, MAX_BUFF_SIZE, 0);
		    if (RecvCount > 0)
		    {
		    	printf("Received %d bytes!\n", RecvCount);
#ifdef PING
		    	send(ConnectSock, (void *)Buffer, RecvCount, 0);
#endif
		    }
		}

		close(ConnectSock);

	    printf("Connection closed!\n");
	}

	close(ConnectSock);

	pthread_exit(NULL);
}

